package com.hyjy.music.util

import android.content.Context
import android.util.Xml
import androidx.annotation.XmlRes
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.w3c.dom.Element
import org.w3c.dom.NodeList
import org.xmlpull.v1.XmlPullParser
import java.io.BufferedReader
import java.io.Closeable
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import javax.xml.parsers.DocumentBuilderFactory

private const val PAIR = "pair"
private const val KEY = "key"

val gson by lazy {
    Gson()
}

/**
 * PULL解析
 */
suspend fun xmlToMap(
    context: Context,
    fileName: String,
    pair: String = PAIR,
    key: String = KEY
): Map<String, String> {
    return runCatching {
        withContext(Dispatchers.IO) {
            context.assets.open(fileName).use {
                val parser = Xml.newPullParser()
                parser.setInput(it, "utf-8")
                mutableMapOf<String, String>().apply {
                    var k: String? = null
                    var v: String? = null
                    var eventType = parser.eventType
                    while (eventType != XmlPullParser.END_DOCUMENT) {
                        when (eventType) {
                            XmlPullParser.START_TAG -> {
                                if (parser.name == pair) {
                                    k = parser.getAttributeValue(null, key)
                                }
                            }
                            XmlPullParser.TEXT -> v = parser.text
                            XmlPullParser.END_TAG -> {
                                if (parser.name == pair) {
                                    if (k != null && v != null) {
                                        this[k] = v
                                        k = null
                                        v = null
                                    }
                                }
                            }
                        }
                        eventType = parser.next()
                    }
                }
            }
        }
    }.getOrElse {
        mapOf()
    }
}


/**
 * PULL解析
 */
suspend fun xmlResToMap(
    context: Context,
    @XmlRes resId: Int,
    pair: String = PAIR,
    key: String = KEY
): Map<String, String> {
    return runCatching {
        withContext(Dispatchers.IO) {
            context.resources.getXml(resId).use {
                mutableMapOf<String, String>().apply {
                    var k: String? = null
                    var v: String? = null
                    var eventType = it.eventType
                    while (eventType != XmlPullParser.END_DOCUMENT) {
                        when (eventType) {
                            XmlPullParser.START_TAG -> {
                                if (it.name == pair) {
                                    k = it.getAttributeValue(null, key)
                                }
                            }
                            XmlPullParser.TEXT -> v = it.text
                            XmlPullParser.END_TAG -> {
                                if (it.name == pair) {
                                    if (k != null && v != null) {
                                        this[k] = v
                                        k = null
                                        v = null
                                    }
                                }
                            }
                        }
                        eventType = it.next()
                    }
                }
            }
        }
    }.getOrElse {
        mapOf()
    }
}

/**
 * 根据名称获取资源id
 */
fun Context.getResourcesIdByName(name: String?, defType: String = "drawable"): Int =
    resources.getIdentifier(name, defType, packageName)

/**
 * DOM解析
 */
suspend fun InputStream.parseXML(tag: String): List<List<String>> {
    return runCatching {
        val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
        val nodeList = documentBuilder.parse(this).documentElement.getElementsByTagName(tag)
        val result = mutableListOf<MutableList<String>>()
        doParse(nodeList = nodeList, result = result)
        result
    }.getOrElse {
        println(it.message)
        mutableListOf()
    }
}

private suspend fun doParse(
    nodeList: NodeList,
    result: MutableList<MutableList<String>>,
    attributes: MutableList<String> = mutableListOf()
) {
    withContext(Dispatchers.IO) {
        for (i in 0 until nodeList.length) {
            val node = nodeList.item(i)
            if (node !is Element) {
                continue
            }
            val attribute = node.getAttribute("name")
            val childNodes = node.childNodes
            if (attribute.isBlank()) {
                if (childNodes.length == 1) {
                    attributes.add(childNodes.item(0).nodeValue)
                }
            } else {
                attributes.add(attribute)
            }
            if (attribute.isBlank()) {
                val temp = mutableListOf<String>()
                temp.addAll(attributes)
                if (result.isNotEmpty()) {
                    val last = result[result.size - 1]
                    val count = last.size - temp.size
                    for (k in 0 until count) {
                        // 缺失元素根据上一个list补齐
                        temp.add(k, last[k])
                    }
                }
                result.add(temp)
                attributes.clear()
            } else {
                doParse(childNodes, result, attributes)
            }
        }
    }
}

fun readConfig(context: Context, fileName: String): String {
    var inputStream: InputStream? = null
    var inputStreamReader: InputStreamReader? = null
    var bufferedReader: BufferedReader? = null
    try {
        inputStream = context.assets.open(fileName)
        inputStreamReader = InputStreamReader(inputStream)
        bufferedReader = BufferedReader(inputStreamReader)
        val stringBuilder = StringBuilder()
        var line: String?
        while (bufferedReader.readLine().apply { line = this } != null) {
            stringBuilder.append(line)
        }
        return stringBuilder.toString()
    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        close(bufferedReader)
        close(inputStreamReader)
        close(inputStream)
    }
    return ""
}

/**
 * 将指定路劲下的json转为实体
 */
inline fun <reified T> jsonToEntity(context: Context, jsonPath: String): T {
    val json = readConfig(context, jsonPath)
    return gson.fromJson(json, T::class.java)
}

fun <T : Closeable> close(stream: T?) {
    runCatching {
        stream?.close()
    }.onFailure {
        println("close onError:$it")
    }
}

//val playModeMap by lazy {
//    mutableMapOf<String, PlayMode>().apply {
//        jsonToEntity<Map<String, String>>(ContextHolder.context, "play_mode.json").forEach {
//            val list = it.value.split(",")
//            if (list.size == 2) {
//                val icon = ContextHolder.context.getResourcesIdByName(list[1])
//                this[it.key] = PlayMode(list[0], icon)
//            }
//        }
//    }
//}
//
//fun Int.toPlayMode(): PlayMode {
//    val size = playModeMap.keys.size
//    val key = (this % size).toString()
//    return playModeMap[key] ?: PlayMode("", 0)
//}
//
///**
// * 播放模式(写在配置文件中实现国际化比较困难，不推荐使用)
// */
//data class PlayMode constructor(var description: String, var icon: Int)